package com.wondersgroup.esclient.util;

import java.lang.annotation.Annotation;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.wondersgroup.esclient.annotation.FieldES;
import com.wondersgroup.esclient.annotation.IdES;
import com.wondersgroup.esclient.common.Constants;
import com.wondersgroup.esclient.emen.QueryType;
import com.wondersgroup.esclient.exception.ElasticSearchClientException;
import com.wondersgroup.esclient.modle.ESData;
import com.wondersgroup.esclient.modle.Pager;
import com.wondersgroup.esclient.modle.QueryParam;
import com.wondersgroup.esclient.util.Reflect.ClassReflect;
import com.wondersgroup.esclient.util.Reflect.FieldReflect;

import io.netty.util.internal.StringUtil;

/**
 * ES请求、响应帮助类
 * @author Walker
 *
 */
public class ESDataUtil {
	
	
	/**
	 * 根据实体对象,产生ES Mappings结构
	 * @param data
	 * @param clazz
	 * @return
	 */
	public static String getMappings(Class<?> clazz) {
		Map<String, Object> mappings = new HashMap<String, Object>();	//mappings层
		Map<String, Object> type = new HashMap<String, Object>();	//type层
		Map<String, Object> properties = new HashMap<String, Object>();	//properties层
		ClassReflect classReflect = Reflect.on(clazz);
		Map<String, FieldReflect> fidlds = classReflect.fields(3);
		Set<Map.Entry<String, FieldReflect>> entrySet = fidlds.entrySet();
		Map<String, Object> propertie = new HashMap<String, Object>();      //属性
		for(Map.Entry<String, FieldReflect> entry : entrySet) {
			Map<String, Object> propertieDefine = new HashMap<String, Object>();  //属性定义
			String fieldName = entry.getKey();
			FieldReflect fieldReflect = entry.getValue();
			//根据自定义注解,IdES FieldES,有这里个注解,增加ES字段
			IdES idES = fieldReflect.getAnnotation(IdES.class);
			FieldES fieldES = fieldReflect.getAnnotation(FieldES.class);
			boolean includeInAll = true;
			if(idES != null || fieldES != null) {
				Class<?> fieldTyep = fieldReflect.fieldType();
				propertieDefine.put("type", Constants.getMappingTyep(fieldTyep));
				//如果是字符串类型
				if(String.class == fieldTyep && fieldES != null) {
					String analyzer = fieldES.analyzer();
					boolean index = fieldES.index();
					boolean keyword = fieldES.keyword();
					includeInAll = fieldES.includeInAll();
					//设定index
					if(index) {
						//设定analyzer 分词器
						if(StringUtil.isNullOrEmpty(analyzer)) {
							//设置为系统默认配置的分词器
							propertieDefine.put("analyzer", Constants.DEFAULT_ANALYZER);
						}else {
							propertieDefine.put("analyzer", analyzer);
						}
						//设置keyword
						if(keyword) {
							propertieDefine.put("type", "keyword");
						}
					}else {
						propertieDefine.put("index", false);
					}
					if(!includeInAll) {
						propertieDefine.put("include_in_all", false);
					}
				}
				if(String.class != fieldTyep && fieldES != null) {
					boolean index = fieldES.index();
					includeInAll = fieldES.includeInAll();
					//设定index
					if(!index) {
						propertieDefine.put("index", index);
					}
				}
				//如果是日期格式，添加格式化参数
				if(Date.class == fieldTyep) {
//					propertieDefine.put("format", Constants.DATE_FORMAT);
				}
				if(!includeInAll) {
					propertieDefine.put("include_in_all", false);
				}
				propertie.put(fieldName, propertieDefine);
			}
		}
		Map<String, Object> propertieDefine = new HashMap<String, Object>();  //属性定义
		propertieDefine.put("type", "text");
		propertieDefine.put("analyzer", Constants.DEFAULT_ANALYZER);  //message指定系统默认分词器
		propertie.put("message", propertieDefine);
		type.put(clazz.getName(), properties);
		properties.put("properties", propertie);
		mappings.put("mappings", type);
		return JSON.toJSON(mappings).toString();
	}

	/**
	 * 根据业务数据,生成入库ES数据
	 * @param data
	 * @param clazz
	 * @return
	 */
	@SuppressWarnings("static-access")
	public static ESData getData(Object data, Class<?> clazz) {
		ESData esData = new ESData();
		JSONObject dataMap = new JSONObject();
		ClassReflect classReflect = Reflect.on(clazz);
		Reflect reflect = classReflect.on(data);
		Map<String, FieldReflect> fidlds = reflect.fields(3);
		Set<Map.Entry<String, FieldReflect>> entrySet = fidlds.entrySet();
		for(Map.Entry<String, FieldReflect> entry : entrySet) {
			String fieldName = entry.getKey();
			FieldReflect fieldReflect = entry.getValue();
			//根据自定义注解,IdES 取_id字段值
			Annotation idES = fieldReflect.getAnnotation(IdES.class);
			Annotation fieldES = fieldReflect.getAnnotation(FieldES.class);
			Object value = fieldReflect.get().off();
			if(idES != null || fieldES != null) {
				Class<?> fieldType = fieldReflect.fieldType();
				//如果是日期格式，做格式转换
				if(Date.class == fieldType) {
					dataMap.put(fieldName, value);
				}else {
					dataMap.put(fieldName, value);
				}
			}
			if(idES != null) {
				esData.set_id(String.valueOf(value));
			}
		}
		dataMap.put("message", JSONObject.toJSONString(data));
		esData.set_index(clazz.getName().toLowerCase());
		esData.set_type(clazz.getName());
		esData.set_data(JSON.toJSONString(dataMap));
		return esData;
	}
	
	public static<T> String getDataByBatch(List<T> entitys, Class<?> clazz) {
		StringBuffer sb = new StringBuffer();
		Map<String, Object> metaDataTemplet = new HashMap<String, Object>();
		metaDataTemplet.put("_index", clazz.getName().toLowerCase());
		metaDataTemplet.put("_type", clazz.getName());
		for(T entity : entitys) {
			Map<String, Object> metaData = new HashMap<String, Object>();    //metaData层
			metaData.putAll(metaDataTemplet);
			ESData esData = getData(entity, clazz);
			metaData.put("_id", esData.get_id());
			Map<String, Object> metaDataWrap = new HashMap<String, Object>(); //包装metaData层
			metaDataWrap.put("index", metaData);
			sb.append(JSON.toJSON(metaDataWrap));
			sb.append(Constants.NEW_LINE);
			sb.append(esData.get_data());
			sb.append(Constants.NEW_LINE);
		}
		return sb.toString();
	}
	
	public static<T> T esDataToObj(JSONObject data, Class<T> clazz){
		ClassReflect classReflect = Reflect.on(clazz);
		Reflect reflect = classReflect.create();
		Map<String, FieldReflect> fidlds = reflect.fields(3);
		Set<Map.Entry<String, FieldReflect>> entrySet = fidlds.entrySet();
		for(Map.Entry<String, FieldReflect> entry : entrySet) {
			String fieldName = entry.getKey();
			FieldReflect fieldReflect = entry.getValue();
			Class<?> fieldType = fieldReflect.fieldType();
			if(Date.class == fieldType) {
				fieldReflect.set(data.getDate(fieldName));
			}else {
				fieldReflect.set(data.get(fieldName));
			}
		}
		T entity = reflect.off();
		return entity;
	}
	
	public static<T> T esDataToObj(String data, Class<T> clazz){
		JSONObject jsonObject = JSON.parseObject(data);
		ClassReflect classReflect = Reflect.on(clazz);
		Reflect reflect = classReflect.create();
		Map<String, FieldReflect> fidlds = reflect.fields(3);
		Set<Map.Entry<String, FieldReflect>> entrySet = fidlds.entrySet();
		for(Map.Entry<String, FieldReflect> entry : entrySet) {
			String fieldName = entry.getKey();
			FieldReflect fieldReflect = entry.getValue();
			Class<?> fieldType = fieldReflect.fieldType();
			if(Date.class == fieldType) {
				fieldReflect.set(jsonObject.getDate(fieldName));
			}else {
				fieldReflect.set(jsonObject.get(fieldName));
			}
		}
		T entity = reflect.off();
		return entity;
	}
	
	
	public static String getQueryParam(List<QueryParam> queryParams) throws ElasticSearchClientException {
		Map<String, Object> param = new HashMap<String, Object>();    //顶层
		Map<String, Object> query = getQueryLayerParam(queryParams);
		param.put("query",query);
		return JSON.toJSONString(param);
	}
	
	public static String getQueryParam(List<QueryParam> queryParams, int from, int size) throws ElasticSearchClientException{
		Map<String, Object> param = new HashMap<String, Object>();    //顶层
		Map<String, Object> query = getQueryLayerParam(queryParams);
		param.put("query",query);
		param.put("from",from);
		param.put("size",size);
		return JSON.toJSONString(param);
	}

	/**
	 * 获取query层查询参数对象
	 * @param queryParams
	 * @return
	 * @throws ElasticSearchClientException 
	 */
	private static Map<String, Object> getQueryLayerParam(List<QueryParam> queryParams) throws ElasticSearchClientException{
		Map<String, Object> query = new HashMap<String, Object>();    //query层
		Map<String, Object> bool = new HashMap<String, Object>();     //bool层
		List<Map<String, Object>> mustList = new ArrayList<Map<String,Object>>();    //must层
		Map<String, Object> mustOpreat = null;              //查询类型层
		Map<String, Object> opreatContent = null;           //查询类型内容层
		Map<String, Object> contentCondition = null;        //查询类型内容条件层
		for(QueryParam queryParam : queryParams) {
			//如果是Match
			if(QueryType.MATCH == queryParam.getQueryType()) {
				mustOpreat = new HashMap<String, Object>();
				opreatContent = new HashMap<String, Object>();   
				opreatContent.put(queryParam.getFieldName(),queryParam.getValue());
				mustOpreat.put(QueryType.MATCH.getCode(), opreatContent);
				mustList.add(mustOpreat);
			}
			//如果是Term
			if(QueryType.TERM == queryParam.getQueryType()) {
				mustOpreat = new HashMap<String, Object>();
				opreatContent = new HashMap<String, Object>();   
				opreatContent.put(queryParam.getFieldName(),queryParam.getValue());
				mustOpreat.put(QueryType.TERM.getCode(), opreatContent);
				mustList.add(mustOpreat);
			}
			//如果是Range   oprateType值不能为空,否则该条件无效
			if(QueryType.RANGE == queryParam.getQueryType() && queryParam.getQueryCondition() != null) {
				mustOpreat = new HashMap<String, Object>();
				opreatContent = new HashMap<String, Object>();   
				contentCondition = new HashMap<String, Object>(); 
				String datePattern = queryParam.getDatePattern();   //如果是日期类型，作一次格式转换
				if(StringUtil.isNullOrEmpty(datePattern)) {
					contentCondition.put(queryParam.getQueryCondition().getCode(), queryParam.getValue());
				}else {
					SimpleDateFormat simpleDateFormat = new SimpleDateFormat(datePattern);
					try {
						Date date = simpleDateFormat.parse(String.valueOf(queryParam.getValue()));
						contentCondition.put(queryParam.getQueryCondition().getCode(), date.getTime());
					} catch (ParseException e) {
						throw new ElasticSearchClientException("QueryParam对象查询时，日期查询参数与日期格式不匹配异常！", e);
					}
				}
				opreatContent.put(queryParam.getFieldName(),contentCondition);
				mustOpreat.put(QueryType.RANGE.getCode(), opreatContent);
				mustList.add(mustOpreat);
			}
			if(QueryType.RANGE == queryParam.getQueryType() && queryParam.getQueryCondition() == null) {
				throw new ElasticSearchClientException("QueryParam对象,使用范围（RANGE）查询时，未指定查询条件（QueryCondition）异常！");
			}
		}
		bool.put("must", mustList);
		query.put("bool", bool);
		return query;
	}
	
	public static<T> List<T> esDataToList(String data, Class<T> clazz){
		JSONObject dataJSON = JSONObject.parseObject(data);
		JSONObject hits = dataJSON.getJSONObject("hits");
		return getEntityList(clazz, hits);
	}
	
	
	public static<T> Pager<T> esDataToPager(String data, Class<T> clazz,Pager<T> pager){
		JSONObject dataJSON = JSONObject.parseObject(data);
		JSONObject hits = dataJSON.getJSONObject("hits");
		pager.setResults(getEntityList(clazz, hits));
		pager.setTotalRow(hits.getInteger("total"));
		return pager;
	}

	private static <T> List<T> getEntityList(Class<T> clazz, JSONObject hits){
		List<T> list = new ArrayList<T>();
		JSONArray datas = hits.getObject("hits", JSONArray.class);
		for(Object object : datas) {
			JSONObject item = (JSONObject) object;
			JSONObject _source = item.getJSONObject("_source");
			String message = _source.getString("message");
			T t = esDataToObj(message, clazz);
			list.add(t);
		}
		return list;
	}

}
